package com.pig4cloud.ila.client.service.impl;

import cn.hutool.core.util.StrUtil;
import com.pig4cloud.ila.client.service.CompilerService;
import com.pig4cloud.pig.common.core.constant.enums.CompilerLanguageEnum;
import org.springframework.stereotype.Service;

import java.io.*;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;

@Service
public class CompilerServiceImpl implements CompilerService {
	private final String shellProgram;
	private final String shellArgument;

	CompilerServiceImpl() {
		if (System.getProperty("os.name").startsWith("Windows")) {
			shellProgram = "cmd";
			shellArgument = "/c";
		} else {
			shellProgram = "bin/sh";
			shellArgument = "-c";
		}
	}

	private String saveFile(String fileName, String code, String type) {
		String fileType = StrUtil.EMPTY;
		if (CompilerLanguageEnum.CPP.getType().equals(type)) {
			fileType = ".cpp";
		} else if (CompilerLanguageEnum.JAVA.getType().equals(type)) {
			fileType = ".java";
		} else if (CompilerLanguageEnum.PYTHON.getType().equals(type)) {
			fileType = ".py";
		} else if (CompilerLanguageEnum.TEXT.getType().equals(type)) {
			fileType = ".txt";
		}
		String writeFilePath = ".\\online-compiler-res\\" + fileName + fileType;
		File file = new File(writeFilePath);
		try {
			boolean isSuccess = file.createNewFile();
			if (isSuccess) {
				Writer writer = new FileWriter(file);
				writer.write(code);
				writer.close();
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		return writeFilePath;
	}

	private boolean compileFile(String filename, String type) {
		List<String> command = new ArrayList<>();
		command.add(shellProgram);
		command.add(shellArgument);
		if (CompilerLanguageEnum.CPP.getType().equals(type)) {
			command.add("g++");
			command.add(filename);
			command.add("-o");
			command.add(filename.replace(".cpp", ""));
		} else if (CompilerLanguageEnum.JAVA.getType().equals(type)) {
			command.add("javac");
			command.add(filename);
		}

		ProcessBuilder processBuilder = new ProcessBuilder(command);

		try {
			Process process = processBuilder.start();
			process.waitFor();
		} catch (Exception e) {
			return false;
		}

		return true;
	}

	private String runCode(String inputFileName, String filename, String type) {
		List<String> command = new ArrayList<>();
		command.add(shellProgram);
		command.add(shellArgument);
		if (CompilerLanguageEnum.CPP.getType().equals(type)) {
			command.add(filename.replace(".cpp", ".exe"));
		} else if (CompilerLanguageEnum.JAVA.getType().equals(type)) {
			command.add("java");
			command.add("Main.class");
		} else if (CompilerLanguageEnum.PYTHON.getType().equals(type)) {
			command.add("python3");
			command.add(filename);
		}
		command.add("<");
		command.add(inputFileName);

		ProcessBuilder processBuilder = new ProcessBuilder(command);
		System.out.println(processBuilder.command());

		StringBuilder result = new StringBuilder();
		try {
			Process process = processBuilder.start();
			BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
			String line;
			System.out.println(System.getProperty("user.dir"));
			while (((line = br.readLine()) != null)) {
				System.out.println(line);
				result.append(line);
				result.append(System.getProperty("line.separator"));
			}
			int exitCode = process.waitFor();
			System.out.println("exitCode = "+exitCode);
		} catch (IOException | InterruptedException e) {
			e.printStackTrace();
		}

		return result.toString();
	}

	public String onHandleCodeFromClient(String input, String code, String type) {
		String fileName = String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")));
		String cppFileName = saveFile(fileName, code, type);

		String result = "";
		if (compileFile(cppFileName, type)) {
			String inputFileName = saveFile(fileName, input, CompilerLanguageEnum.TEXT.getType());
			result = runCode(inputFileName, cppFileName, type);
		}

		return result;
	}
}
